﻿using System.Reflection;
using System.Xml.Linq;
using AutoMoq;
using LINQPad.Extensibility.DataContext;
using NUnit.Framework;
using FluentAssertions;
using System.Linq;

namespace MongoLP.Test
{
    [TestFixture]
    public class DynamicDriverTests
    {
        public DynamicDriver Driver { get; set; }

        [SetUp]
        public void Setup()
        {
            Driver = new DynamicDriver();
        }

        [Test]
        public void NameIsCorrect()
        {
            Driver.Name.Should().NotBeNullOrEmpty();
            Driver.Name.Should().Be("MongoLP - Dynamic LinqPad Driver");
            Driver.Name.Should().NotBe("something else");
        }

        [Test]
        public void AuthorIsCorrect()
        {
            Driver.Author.Should().NotBeNullOrEmpty();
            Driver.Author.Should().Be("Paul Saunders");
        }

        [Test]
        public void GetAssembliesShouldReturnAListOfAssemblyNames()
        {
            var driverData = new XElement("DriverData",
                    new XElement("ConnectionString", "mongodb://localhost/MyDatabase"),
                    new XElement("EntityAssemblyPath", @"C:\test\test.dll"),
                    new XElement("EntityInterface", @"IEntity")
                );

            var mock = new AutoMoqer();
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.AppConfigPath)
                .Returns("\\TestPath");
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.DriverData)
                .Returns(driverData);
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.CustomTypeInfo.CustomAssemblyPath)
                .Returns(@"C:\test\test.dll");
            
            // need to do this for the dodgy piece of side effect it gives us. :-(
            Driver.GetConnectionDescription(mock.Resolve<IConnectionInfo>());

            var assemblies = Driver.GetAssembliesToAdd();
            assemblies.Should().NotBeNull();
            foreach(var a in assemblies)
            {
                a.Should().NotBeNullOrEmpty();
            }

            assemblies.Count().Should().Be(5);
            assemblies.Count(a => a.Contains("MongoDB")).Should().Be(2);
            assemblies.Count(a => a.Contains("Mongo")).Should().Be(4);
            assemblies.Count(a => a.Contains("Dream")).Should().Be(1);
            assemblies.Count(a => a.Contains("test.dll")).Should().Be(1);
        }

        [Test]
        public void CetConstructorArgumentsWorks()
        {
            var driverData = new XElement("DriverData",
                    new XElement("ConnectionString", "mongodb://localhost/MyDatabase"),
                    new XElement("EntityAssemblyPath", @"C:\test\test.dll"),
                    new XElement("EntityInterface", @"IEntity")
                );

            var mock = new AutoMoqer();
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.AppConfigPath)
                .Returns("\\TestPath");
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.DriverData)
                .Returns(driverData);
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.CustomTypeInfo.CustomAssemblyPath)
                .Returns(@"C:\test\test.dll");

            var args = Driver.GetContextConstructorArguments(mock.Resolve<IConnectionInfo>());
            args.Count().Should().Be(1);
            args[0].Should().BeAssignableTo<MongoDB.Driver.MongoDatabase>();
            args[0].As<MongoDB.Driver.MongoDatabase>().Name.Should().Be("MyDatabase");
        }

        [Test]
        public void GetConstructorParametersWorks()
        {
            var driverData = new XElement("DriverData",
                    new XElement("ConnectionString", "mongodb://localhost/MyDatabase"),
                    new XElement("EntityAssemblyPath", @"C:\test\test.dll"),
                    new XElement("EntityInterface", @"IEntity")
                );

            var mock = new AutoMoqer();
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.AppConfigPath)
                .Returns("\\TestPath");
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.DriverData)
                .Returns(driverData);
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.CustomTypeInfo.CustomAssemblyPath)
                .Returns(@"C:\test\test.dll");

            var args = Driver.GetContextConstructorParameters(mock.Resolve<IConnectionInfo>());
            args.Count().Should().Be(1);
            args[0].ParameterName.Should().Be("mongo");
            args[0].FullTypeName.Should().Contain("MongoDatabase");
        }

        [Test]
        public void GetNamespacesToAddShouldReturnAListofNamespaces()
        {
            var namespaces = Driver.GetNamespacesToAdd();

            namespaces.Count().Should().Be(4);
            namespaces.Count(ns => ns.Contains("Mongo")).Should().Be(4);
            namespaces.Count(ns => ns.Contains("Dream")).Should().Be(1);
        }

        [Test]
        public void GetSchemaAndBuildWorks()
        {
            var driverData = new XElement("DriverData",
                    new XElement("ConnectionString", "mongodb://localhost/MyDatabase"),
                    new XElement("EntityAssemblyPath", @"MongoLP.Test.dll"),
                    new XElement("EntityInterface", @"DreamSongs.MongoRepository.IEntity")
                );

            var mock = new AutoMoqer();
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.AppConfigPath)
                .Returns("\\TestPath");
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.DriverData)
                .Returns(driverData);
            mock.GetMock<IConnectionInfo>()
                .Setup(x => x.CustomTypeInfo.CustomAssemblyPath)
                .Returns(@"C:\test\test.dll");

            var nameSpace = @"LinqPadDriverTest";
            var typeName = @"MongoDbDriver";

            var list = Driver.GetSchemaAndBuildAssembly(mock.Resolve<IConnectionInfo>(), new AssemblyName(), ref nameSpace, ref typeName);
            list.Count().Should().Be(2);
            list.Count(x => x.Kind == ExplorerItemKind.QueryableObject).Should().Be(1);
            list.Count(x => x.Kind == ExplorerItemKind.CollectionLink).Should().Be(1);
        }
    }
}
